#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#include "token.h"

static struct itimerval olditv;
static struct sigaction oldact;
static struct mytbf_t *job[MYTBF_MAX];
static int firstInit = 1;

struct mytbf_t
{
	int cps;
	int token;
	int burst;
	int pos;
};

void alrmAction(int s, siginfo_t *infop, void *unused)
{
	if (infop->si_code != SI_KERNEL)
		return ;
	int i;

	for (i = 0; i < MYTBF_MAX; i++)
	{
		if (job[i] != NULL)
		{
			job[i]->token += job[i]->cps;
			if (job[i]->token > job[i]->burst)
			{
				job[i]->token = job[i]->burst;
			}
		}
	}
}

static int getFreePos()
{
	int i;

	for (i = 0; i < MYTBF_MAX; i++)
	{
		if (job[i] == NULL)
		{
			return i;
		}
	}
	return -1;
}

static void module_unload()
{
	int i;

	sigaction(SIGALRM, &oldact, NULL);	
	setitimer(ITIMER_REAL, &olditv, NULL);

	for (i = 0; i < MYTBF_MAX; i++)
	{
		free(job[i]);
	}	
}

static void module_load()
{
	struct itimerval itv;
	struct sigaction act;
	
	act.sa_sigaction = alrmAction;
	sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO;
	
	itv.it_interval.tv_sec = 1;
	itv.it_interval.tv_usec = 0;
	itv.it_value.tv_sec = 1;
	itv.it_value.tv_usec = 0;

	sigaction(SIGALRM, &act, &oldact);	
	setitimer(ITIMER_REAL, &itv, &olditv);
	
	atexit(module_unload);	
}


mytbf_t *mytbfInit(int cps, int burst)
{
	struct mytbf_t *me;
	int pos;

	if (firstInit)
	{
		module_load();
		firstInit = 0;
	}
	
	pos = getFreePos();
	if (pos < 0)
	{
		return NULL;
	}

	me = malloc(sizeof(struct mytbf_t));
	if (me == NULL)
	{
		fprintf(stdout, "malloc() false!\n");
		return NULL;
	}

	me->cps = cps;
	me->burst = burst;
	me->token = 0;
	me->pos = pos;

	job[pos] = me;
	return me;
}

static int min(int a, int b)
{
	return a < b ? a : b;
}

int mytbfFetchToken(mytbf_t *ptr, int size)
{
	struct mytbf_t *me = ptr;

	int n;

	if (size <= 0)
	{
		return -EINVAL;
	}
	
	while (me->token <= 0)
		pause();
	n = min(me->token, size);
	
	me->token -= n;
	return n;
}

int mytbfReturnToken(mytbf_t *ptr, int size)
{
	struct mytbf_t *me = ptr;

	if (size <= 0)
	{
		return -EINVAL;
	}
	
	me->token += size;
	if (me->token > me->burst)
	{
		me->token = me->burst;
	}
	return size;
}

int mytbfDestory(mytbf_t *ptr)
{
	struct mytbf_t *me = ptr;

	job[me->pos] = NULL;
	free(ptr);
	
	return 0;
}
